#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

#define EPOLLSIZE 10
#define LINE 1024

int handle(int connfd); // 处理连接请求
int setnonblocking(int sockfd); // 设置socket为非阻塞模式

int main(int argc, char **argv) {
    int servPort = 6888;
    int listenq = 1024; // 允许的最大连接请求数
    int listenfd, connfd, epollfd, nfds, n, nread, curfds, acceptcount = 0;
    struct sockaddr_in servaddr, cliaddr; // 服务器地址和客户端地址
    socklen_t socklen = sizeof(struct sockaddr_in);
    struct epoll_event ev;
    struct epoll_event events[EPOLLSIZE];
    char buf[LINE]; // 数据缓冲区，保存接收数据

    memset(servaddr.sin_zero, 0, sizeof(servaddr.sin_zero));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(servPort);

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("create socket failed");
        return -1;
    }

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (setnonblocking(listenfd) < 0) {
        perror("setnonblocking error");
        return -1;
    }

    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)) < 0) {
        perror("bind error");
        return -1;
    }

    if (listen(listenfd, listenq) < 0) {
        perror("listen error");
        return -1;
    }

    epollfd = epoll_create(EPOLLSIZE);
    ev.events = EPOLLIN | EPOLLET; // 监听是否有数据可读
    ev.data.fd = listenfd; // 检查监听socket是否可操作
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev) < 0) {
        fprintf(stderr, "epoll set insertion error: fd=%d\n", listenfd);
        return -1;
    }

    curfds = 1; // 当前文件描述符集合
    printf("epoll server startup, port=%d\n", servPort);
    for (;;) {
        nfds = epoll_wait(epollfd, events, EPOLLSIZE, -1); // 等待事件的到来
        if (nfds < 0) {
            perror("epoll wait");
            continue;
        }

        for (n = 0; n < nfds; n++) {
            if (events[n].data.fd == listenfd) { // 如果描述符为监听socket
                connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &socklen); // 接收请求并建立连接
                if (connfd < 0) {
                    perror("accept error");
                    continue;
                }
                sprintf(buf, "accept from %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
                printf("the connection: %d, %s\n", ++acceptcount, buf);
                if (curfds > EPOLLSIZE) {
                    fprintf(stderr, "too many connections, more than %d\n", EPOLLSIZE);
                    close(connfd);
                    continue;
                }
                if (setnonblocking(connfd) < 0) {
                    perror("setnonblocking error");
                }
                ev.events = EPOLLIN | EPOLLET; // 将连接socket放入到监控对象中
                ev.data.fd = connfd;
                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) < 0) {
                    perror("epoll_ctl failed");
                    return -1;
                }
                curfds++;
                continue; // 继续检查监听和连接socket
            }

            if (handle(events[n].data.fd) < 0) {
                epoll_ctl(epollfd, EPOLL_CTL_DEL, events[n].data.fd, &ev);
                curfds--;
            }
        }
    }

    close(listenfd);
    return 0;
}

int handle(int connfd) {
    int nread;
    char buf[LINE];
    nread = read(connfd, buf, LINE); // 读取客户端socket流数据
    if (nread == 0) {
        printf("client close the connection\n");
        close(connfd);
        return -1;
    }
    if (nread < 0) {
        perror("read error");
        close(connfd);
        return -1;
    }
    return 0;
}

int setnonblocking(int sockfd) {
    int flag;
    if ((flag = fcntl(sockfd, F_GETFL, 0)) < 0) {
        perror("get flag error");
        return -1;
    }
    flag |= O_NONBLOCK;
    if (fcntl(sockfd, F_SETFL, flag) < 0) {
        perror("set flag error");
        return -1;
    }
    return 0;
}